<template>
  <div class="slide-circus_wrap">
    <div class="main-content">
      <div class="sliding-pictures">
        <div
          id="captcha"
          v-loading="showLoading"
          style="min-height: 102px"
          element-loading-spinner="el-icon-loading"
          element-loading-background="rgba(255,255,255,.8)"
        >
          <div class="rect-icon" @click="onRefresh" />
        </div>
      </div>
    </div>
    <div class="slider-container">
      <div class="slider-mask">
        <div class="slider-el">
          <span class="slider-icon" />
        </div>
      </div>
      <span class="slider-text">向右拖动滑块完成验证</span>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showLoading: true,
      canvasCtx: null,
      blockCtx: null,
      blockDom: null,
      sliderDom: null,
      sliderContainerDom: null,
      sliderMaskDom: null,
      el: null,
      img: null,
      trail: null,
      cv: {
        w: 380,
        h: 102
      },
      block: {
        l: 38,
        L: 38 + 9 * 2 + 3,
        r: 8
      },
      randomPos: {
        x: 0,
        y: 0
      }
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.canvasInit();
      this.initImg();
      this.bindEvents();
    },
    onRefresh() {
      this.$emit('handleCheck', false);
      this.reset();
    },
    onSuccess() {
      this.$emit('handleCheck', true);
    },
    onFail() {
      this.$emit('handleCheck', false);
    },
    bindEvents() {
      this.el.onselectstart = () => false;
      let originX;
      let originY;
      const trail = [];
      let isMouseDown = false;
      const addClass = function(tag, className) {
        tag.classList.add(className);
      };
      const removeClass = function(tag, className) {
        tag.classList.remove(className);
      };
      const handleDragStart = function(e) {
        originX = e.clientX || e.touches[0].clientX;
        originY = e.clientY || e.touches[0].clientY;
        if (this.img) {
          isMouseDown = true;
        }
      }.bind(this);

      const handleDragMove = e => {
        if (!isMouseDown) return false;
        const eventX = e.clientX || e.touches[0].clientX;
        const eventY = e.clientY || e.touches[0].clientY;
        const moveX = eventX - originX;
        const moveY = eventY - originY;
        if (moveX < 0 || moveX + 32 >= this.cv.w) return false;
        this.sliderDom.style.left = moveX + 'px';
        const blockLeft = ((this.cv.w - 40 - 20) / (this.cv.w - 40)) * moveX;
        this.blockDom.style.left = blockLeft + 'px';

        addClass(this.sliderContainerDom, 'slider-container_active');
        this.sliderMaskDom.style.width = moveX + 'px';
        trail.push(moveY);
      };

      const handleDragEnd = e => {
        if (!isMouseDown) return false;
        isMouseDown = false;
        const eventX = e.clientX || e.changedTouches[0].clientX;
        if (eventX == originX) return false;
        removeClass(this.sliderContainerDom, 'slider-container_active');
        this.trail = trail;
        const { spliced, verified } = this.verify();
        if (spliced) {
          if (verified) {
            addClass(this.sliderContainerDom, 'slider-container_success');
            this.onSuccess();
          } else {
            addClass(this.sliderContainerDom, 'slider-container_fail');
            this.onFail();
            this.reset();
          }
        } else {
          addClass(this.sliderContainerDom, 'slider-container_fail');
          this.onFail();
          setTimeout(() => {
            this.reset();
          }, 1000);
        }
      };
      this.sliderDom.addEventListener('mousedown', handleDragStart);
      this.sliderDom.addEventListener('touchstart', handleDragStart);
      this.blockDom.addEventListener('mousedown', handleDragStart);
      this.blockDom.addEventListener('touchstart', handleDragStart);
      document.addEventListener('mousemove', handleDragMove);
      document.addEventListener('touchmove', handleDragMove);
      document.addEventListener('mouseup', handleDragEnd);
      document.addEventListener('touchend', handleDragEnd);
    },
    verify() {
      const sum = function(x, y) {
        return x + y;
      };
      const square = function(x) {
        return x * x;
      };

      const arr = this.trail; // 拖动时y轴的移动距离
      const average = arr.reduce(sum) / arr.length;
      const deviations = arr.map(x => x - average);
      const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length);
      const left = parseInt(this.blockDom.style.left);
      return {
        spliced: Math.abs(left - this.randomPos.x) < 10,
        verified: stddev !== 0 // 简单验证下拖动轨迹，为零时表示Y轴上下没有波动，可能非人为操作
      };
    },
    reset() {
      this.showLoading = true;
      this.sliderContainerDom.className = 'slider-container';
      this.sliderDom.style.left = 0;
      this.blockDom.style.left = 0;
      this.sliderMaskDom.style.width = 0;
      this.clean();
      this.img.setSrc(this.getRandomImgSrc());
    },
    clean() {
      this.canvasCtx.clearRect(0, 0, this.cv.w, this.cv.h);
      this.blockCtx.clearRect(0, 0, this.cv.w, this.cv.h);
      this.blockDom.width = this.cv.w;
    },
    draw() {
      // 随机创建滑块的位置
      this.randomPos.x = this.getRandomNumberByRange(this.block.L + 10, this.cv.w - (this.block.L + 10));
      this.randomPos.y = this.getRandomNumberByRange(10 + this.block.r * 2, this.cv.h - (this.block.L + 10));
      this.drawBlock(this.canvasCtx, this.randomPos.x, this.randomPos.y, 'fill');
      this.drawBlock(this.blockCtx, this.randomPos.x, this.randomPos.y, 'clip');
    },
    drawBlock(ctx, x, y, operation) {
      const r = this.block.r;
      const l = this.block.l;
      const PI = Math.PI;

      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
      ctx.lineTo(x + l, y);
      ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
      ctx.lineTo(x + l, y + l);
      ctx.lineTo(x, y + l);
      ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
      ctx.lineTo(x, y);
      ctx.lineWidth = 2;
      ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
      ctx.stroke();
      ctx[operation]();
      ctx.globalCompositeOperation = 'destination-over';
    },
    initImg() {
      const img = this.createImg(() => {
        this.draw();
        this.canvasCtx.drawImage(img, 0, 0, this.cv.w, this.cv.h);
        this.blockCtx.drawImage(img, 0, 0, this.cv.w, this.cv.h);
        const y = this.randomPos.y - this.block.r * 2 - 1;
        const ImageData = this.blockCtx.getImageData(this.randomPos.x - 3, y, this.block.L, this.block.L);
        this.blockDom.width = this.block.L;
        this.blockCtx.putImageData(ImageData, 0, y);
      });
      this.img = img;
    },
    canvasInit() {
      const canvas = this.createCanvas(this.cv.w, this.cv.h); // 画布
      const block = canvas.cloneNode(true); // 滑块
      block.className = 'block';
      block.style.position = 'absolute';
      block.style.left = 0;
      block.style.top = 0;
      block.style.cursor = 'grab';
      const el = (this.el = document.getElementById('captcha'));
      el.style.position = 'relative';
      el.style.width = '100%';
      Object.assign(el.style, {
        position: 'relative',
        width: '100%'
      });
      this.el = el;
      el.appendChild(canvas);
      el.appendChild(block);

      this.canvasCtx = canvas.getContext('2d');
      this.blockCtx = block.getContext('2d');
      this.blockDom = block;
      this.sliderDom = document.getElementsByClassName('slider-el')[0];
      this.sliderContainerDom = document.getElementsByClassName('slider-container')[0];
      this.sliderMaskDom = document.getElementsByClassName('slider-mask')[0];
    },
    createCanvas(width, height) {
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      return canvas;
    },
    createImg(onload) {
      const img = new Image();
      img.crossOrigin = 'Anonymous';
      img.onload = onload;
      img.onerror = () => {
        img.setSrc(this.getRandomImgSrc());
      };
      img.setSrc = src => {
        // IE浏览器无法通过img.crossOrigin跨域，使用ajax获取图片blob然后转为dataURL显示
        if (window.navigator.userAgent.indexOf('Trident') > -1) {
          const xhr = new XMLHttpRequest();
          xhr.onloadend = e => {
            const file = new FileReader(); // FileReader仅支持IE10+
            file.readAsDataURL(e.target.response);
            file.onloadend = function(e) {
              img.src = e.target.result;
            };
          };
          xhr.open('GET', src);
          xhr.responseType = 'blob';
          xhr.send();
        } else img.src = src;
      };
      img.setSrc(this.getRandomImgSrc());
      return img;
    },

    getRandomNumberByRange(start, end) {
      return Math.round(Math.random() * (end - start) + start);
    },
    getRandomImgSrc() {
      // const imgUrl = 'https://picsum.photos/320/150/?image=' + this.getRandomNumberByRange(0, 1084); //内网无法访问
      // const randomNum = Math.ceil(Math.random() * 2);
      const randomNum = 1;
      const imgUrl = require(`@/assets/imgs/slideCircus/img_${randomNum}.png`);
      const image = new Image();
      image.src = imgUrl;
      image.onerror = () => {
        this.showLoading = false;
      };
      image.onload = () => {
        this.showLoading = false;
      };
      return imgUrl;
    }
  }
};
</script>
<style lang="scss">
.slide-circus_wrap {
  .main-content {
    .sliding-pictures {
      position: relative;

      i {
        position: absolute;
        right: 7%;
        z-index: 9;
        font-size: 0.22rem;
      }
    }
  }

  .rect-icon {
    width: 30px;
    height: 30px;
    position: absolute;
    right: 2px;
    top: 0px;
    z-index: 99999;
    background: url('../../../assets/imgs/slideCircus/spriteImg.png') 0 -910px;
    cursor: pointer;
  }

  .slider-icon {
    display: inline-block;
    width: 32px;
    height: 12px;
    background: url('../../../assets/imgs/slideCircus/spriteImg.png') 8px -24px;
    background-size: 34px 471px;
  }

  .refreshIcon {
    position: absolute;
    right: 0;
    top: 0;
    width: 34px;
    height: 34px;
    cursor: pointer;
    background: url('../../../assets/imgs/slideCircus/spriteImg.png') 0 -437px;
    background-size: 34px 471px;
  }

  .block {
    position: absolute;
    left: 0;
    top: 0;
    cursor: pointer;
    cursor: grab;
  }

  .block:active {
    cursor: pointer;
    cursor: grabbing;
  }

  .slider-container {
    position: relative;
    text-align: center;
    height: 32px;
    line-height: 32px;
    background: #f7f9fa;
    color: #45494c;
    box-shadow: 0 0 2px #ccc;
  }

  .slider-text {
    font-size: 12px;
    font-weight: 400;
    color: rgba(58, 61, 73, 0.4);
  }

  .slider-container_active .slider {
    height: 32px;
  }

  .slider-container_active .slider-mask {
    height: 32px;
    border-width: 1px;
  }

  .slider-container_success .slider {
    height: 32px;
    margin-left: -1px;
    background-color: #66b1ff !important;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .slider-container_success .slider-mask {
    height: 32px;
  }

  .slider-container_success .slider-icon {
    background-position: 8px -67px !important;
  }

  .slider-container_fail .slider {
    height: 32px;
    background-color: #f57a7a !important;
  }

  .slider-container_fail .slider-mask {
    height: 32px;
    box-shadow: 0 0 6px #f57a7a;
    background-color: #fce1e1;
  }

  .slider-container_fail .slider-icon {
    top: 14px;
    background-position: 8px -97px !important;
  }

  .slider-container_active .slider-text,
  .slider-container_success .slider-text,
  .slider-container_fail .slider-text {
    display: none;
  }

  .slider-mask {
    position: absolute;
    left: 0;
    top: 0;
    height: 32px;
    background: rgba(209, 232, 253, 0.6);
  }

  .slider-el {
    position: absolute;
    top: 0;
    left: 0;
    width: 32px;
    height: 32px;
    background: #fff;
    transition: background 0.2s linear;
    cursor: move;
    cursor: grab;
    box-shadow: 0 0 6px #ccc;
  }

  .slider-el:active {
    cursor: grabbing;
  }

  .slider-el:hover {
    background: #1991fa;
    box-shadow: 0 0 6px #1991fa;
  }

  .slider-el:hover .slider-icon {
    background-position: 7px -11px;
  }
}
</style>
